#include "Display.h"


#define DISP_CS(x)  DisplayHAL_GPIO_Write(DISP_IO_CS,x);
#define DISP_DC(x)  DisplayHAL_GPIO_Write(DISP_IO_DC,x)
#define DISP_CMD(x) \
    DISP_DC(0);DISP_CS(0);DisplayHAL_SPI_Transmit8(x);DISP_CS(1);
#define DISP_DAT(x) \
    DISP_DC(1);DISP_CS(0);DisplayHAL_SPI_Transmit8(x);DISP_CS(1);
#define DISP_DAT16(x) \
    DISP_DC(1);DISP_CS(0);DisplayHAL_SPI_Transmit8((x >> 8) & 0xff);DisplayHAL_SPI_Transmit8(x & 0xff);DISP_CS(1);


/****************************************
 * 屏幕偏移量设置
    对于同型号不同显示屏有不同的偏移量，需要单独进行配置
    一般情况下将排线底下的一边作为宽度
 ****************************************/
#if defined(DISP_USE_ST7789_1IN14_LCD)

#elif defined(DISP_USE_ST7789_1IN69_LCD)

#elif defined(DISP_USE_ST7789_2INCH_LCD)
    #define DISP_USE_ST7789
    #define DISP_XOFFSET    0
    #define DISP_YOFFSET    0
    #define DISP_WIDTH      240
    #define DISP_HEIGHT     320
#endif

void Display_Reset()
{
    DisplayHAL_GPIO_Write(DISP_IO_RST,1);
    DisplayHAL_SYS_Delay(100);
    DisplayHAL_GPIO_Write(DISP_IO_RST,0);
    DisplayHAL_SYS_Delay(100);
    DisplayHAL_GPIO_Write(DISP_IO_RST,1);
    DisplayHAL_SYS_Delay(200);
}

void Display_Init()
{
    Display_Reset();
#ifdef DISP_USE_ST7789
    DISP_CMD(0xB2);
    DISP_DAT(0x0C);
    DISP_DAT(0x0C);
    DISP_DAT(0x00);
    DISP_DAT(0x33);
    DISP_DAT(0x33);

    DISP_CMD(0xB7);  //Gate Control
    DISP_DAT(0x35);

    DISP_CMD(0xBB);  //VCOM Setting
    DISP_DAT(0x1F);

    DISP_CMD(0xC0); //LCM Control     
    DISP_DAT(0x2C);

    DISP_CMD(0xC2);  //VDV and VRH Command Enable
    DISP_DAT(0x01);
    DISP_CMD(0xC3);  //VRH Set
    DISP_DAT(0x12);
    DISP_CMD(0xC4);  //VDV Set
    DISP_DAT(0x20);

    DISP_CMD(0xC6);  //Frame Rate Control in Normal Mode
    DISP_DAT(0x0F);
    
    DISP_CMD(0xD0);  // Power Control 1
    DISP_DAT(0xA4);
    DISP_DAT(0xA1);

    DISP_CMD(0xE0);  //Positive Voltage Gamma Control
    // 总共有两套Gamma配置，怎么用看你习惯
    // DISP_DAT(0xD0);
    // DISP_DAT(0x04);
    // DISP_DAT(0x0D);
    // DISP_DAT(0x11);
    // DISP_DAT(0x13);
    // DISP_DAT(0x2B);
    // DISP_DAT(0x3F);
    // DISP_DAT(0x54);
    // DISP_DAT(0x4C);
    // DISP_DAT(0x18);
    // DISP_DAT(0x0D);
    // DISP_DAT(0x0B);
    // DISP_DAT(0x1F);
    // DISP_DAT(0x23);

    DISP_DAT(0xD0);
	DISP_DAT(0x08);
	DISP_DAT(0x11);
	DISP_DAT(0x08);
	DISP_DAT(0x0C);
	DISP_DAT(0x15);
	DISP_DAT(0x39);
	DISP_DAT(0x33);
	DISP_DAT(0x50);
	DISP_DAT(0x36);
	DISP_DAT(0x13);
	DISP_DAT(0x14);
	DISP_DAT(0x29);
	DISP_DAT(0x2D);

    DISP_CMD(0xE1);  //Negative Voltage Gamma Control
    // DISP_DAT(0xD0);
    // DISP_DAT(0x04);
    // DISP_DAT(0x0C);
    // DISP_DAT(0x11);
    // DISP_DAT(0x13);
    // DISP_DAT(0x2C);
    // DISP_DAT(0x3F);
    // DISP_DAT(0x44);
    // DISP_DAT(0x51);
    // DISP_DAT(0x2F);
    // DISP_DAT(0x1F);
    // DISP_DAT(0x1F);
    // DISP_DAT(0x20);
    // DISP_DAT(0x23);

    DISP_DAT(0xD0);
	DISP_DAT(0x08);
	DISP_DAT(0x10);
	DISP_DAT(0x08);
	DISP_DAT(0x06);
	DISP_DAT(0x06);
	DISP_DAT(0x39);
	DISP_DAT(0x44);
	DISP_DAT(0x51);
	DISP_DAT(0x0B);
	DISP_DAT(0x16);
	DISP_DAT(0x14);
	DISP_DAT(0x2F);
	DISP_DAT(0x31);

    DISP_CMD(0x21);  //Display Inversion On

    DISP_CMD(0x11);  //Sleep Out
    DisplayHAL_SYS_Delay(120);
    DISP_CMD(0x29);  //Display On

    DISP_CMD(0x3A);
    DISP_DAT(0x05); //设置为RGB565模式，这段命令需要塞到最后

#else
    #error "There's no display selected!"
#endif
}

void Display_SetWindow(DisplayDirectionEnum_Typedef dir,uint16_t xStart,uint16_t xEnd,uint16_t yStart,uint16_t yEnd)
{
    if(dir == DISP_DIR_0_DEG)
    {
        xStart  += DISP_XOFFSET;
        xEnd    += DISP_XOFFSET;
        yStart  += DISP_YOFFSET;
        yEnd    += DISP_YOFFSET;
    }
    else
    {
        xStart  += DISP_YOFFSET;
        xEnd    += DISP_YOFFSET;
        yStart  += DISP_XOFFSET;
        yEnd    += DISP_XOFFSET;
    }
#ifdef DISP_USE_ST7789_1IN14_LCD
    // 有可能是ST7789显示屏的工艺问题,这玩意好像有点偏差
    if(dir == DISP_DIR_90_DEG)
    {
        xStart  -= 1;
        xEnd    -= 1;
    }
#endif
    
#ifdef DISP_USE_ST7789
    if(dir == DISP_DIR_0_DEG)
    {
        DISP_CMD(0x36);
        DISP_DAT(0x00);
    }
    else
    {
        DISP_CMD(0x36);
        DISP_DAT(0x70);
    }
    DISP_CMD(0x2A);
    // 列扫描寄存器
    DISP_DAT16(xStart);
    DISP_DAT16(xEnd);
    DISP_CMD(0x2B);
    // 行扫描寄存器
    DISP_DAT16(yStart);
    DISP_DAT16(yEnd);
    // 开始传输数据
    DISP_CMD(0x2C);
#else
    #error "There's no display selected!"
#endif
    DISP_CS(1);
}

void Display_Clear(uint16_t color)
{
    uint16_t x,y;
    // 默认采用垂直方向填充
    Display_SetWindow(DISP_DIR_0_DEG,0,DISP_WIDTH-1,0,DISP_HEIGHT-1);
    DISP_DC(1); DISP_CS(0);
    for(y=0;y<DISP_HEIGHT;y++)
    {
        for(x=0;x<DISP_WIDTH;x++)
        {
            #ifdef DISP_SWAP_2BYTE
                DisplayHAL_SPI_Transmit8(color & 0xff);
                DisplayHAL_SPI_Transmit8(color >> 8);
            #else
                // DisplayHAL_SPI_Transmit8(0xff);
                DisplayHAL_SPI_Transmit8((color >> 8) & 0xff);
                DisplayHAL_SPI_Transmit8(color & 0xff);
            #endif
        }
    }
    DISP_CS(1);
}

uint8_t Display_FlushArea_isFinalTransaction;

void Display_FlushArea(DisplayDirectionEnum_Typedef dir,uint16_t xStart,uint16_t xEnd,uint16_t yStart,uint16_t yEnd,uint16_t *pixelBuffer)
{
    // 渲染一个区域
    Display_SetWindow(dir,xStart,xEnd,yStart,yEnd);
    DISP_DC(1); DISP_CS(0);
    #if defined(DISP_USE_DMA)
    // 计算剩余的字节数
    uint32_t bytesLeft = (xEnd-xStart+1) * (yEnd-yStart+1) * sizeof(uint16_t);
    uint8_t *dmaBuffer = (uint8_t *)pixelBuffer;
    while(bytesLeft > DISP_DMA_BUFSIZE)
    {
        Display_FlushArea_isFinalTransaction = 0;
        DisplayHAL_SPI_Transmit_DMA(dmaBuffer,DISP_DMA_BUFSIZE);
        DisplayHAL_SPI_Wait_DMA();
        bytesLeft -= DISP_DMA_BUFSIZE;
        dmaBuffer += DISP_DMA_BUFSIZE;
    }
    Display_FlushArea_isFinalTransaction = 1;
    DisplayHAL_SPI_Transmit_DMA(dmaBuffer,bytesLeft);
    DisplayHAL_SPI_Wait_DMA();
    DISP_CS(1);
    #else
    uint16_t x,y;
    for(y=yStart;y<=yEnd;y++)
    {
        for(x = xStart;x<=xEnd;x++)
        {
            #ifdef DISP_SWAP_2BYTE
                DisplayHAL_SPI_Transmit8((*pixelBuffer) & 0xff);
                DisplayHAL_SPI_Transmit8((*pixelBuffer) >> 8);
            #else
                DisplayHAL_SPI_Transmit8((*pixelBuffer) >> 8);
                DisplayHAL_SPI_Transmit8((*pixelBuffer) & 0xff);
            #endif
            pixelBuffer++;
        }
    }
    DISP_CS(1);
    #endif
}

void Display_SetBackLight(bool value)
{
    DisplayHAL_GPIO_Write(DISP_IO_BL,value);
}